package com.kk.crowd.mvc.config;

import com.google.gson.Gson;
import com.kk.crowd.constant.CrowdConstant;
import com.kk.crowd.exception.AccessForbiddenException;
import com.kk.crowd.exception.LoginAcctAlreadyInUseException;
import com.kk.crowd.exception.LoginFailedException;
import com.kk.crowd.util.CrowdUtil;
import com.kk.crowd.util.ResultEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// @ControllerAdvice表示当前类是一个基于注解的异常处理器类
@ControllerAdvice
public class CrowdExceptionResolver {
    // 登录异常处理(每个新异常需要更改三个地方，其他复制)
    // 1、value = xxx
    // 2、第一个参数
    // 3、viewName


    // 基于注解的异常映射和基于 XML 的异常映射如果映射同一个异常类型， 那么基于注解的方案优先。


    @ExceptionHandler(value = LoginAcctAlreadyInUseException.class)
    public ModelAndView resolveMathException(
            LoginAcctAlreadyInUseException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "admin-add";
        return commonResolve (viewName, exception, request, response);
    }

    @ExceptionHandler(value = AccessForbiddenException.class)
    public ModelAndView resolveMathException(
            AccessForbiddenException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "admin-login";
        return commonResolve (viewName, exception, request, response);
    }

    @ExceptionHandler(value = LoginFailedException.class)
    public ModelAndView resolveMathException(
            LoginFailedException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "admin-login";
        return commonResolve (viewName, exception, request, response);
    }

    // 算数异常处理
    @ExceptionHandler(value = ArithmeticException.class)
    public ModelAndView resolveMathException(
            ArithmeticException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "system-error";
        return commonResolve (viewName, exception, request, response);
    }

    // 空指针异常处理
    @ExceptionHandler(value = NullPointerException.class)
    public ModelAndView resolveNullPointerException(
            NullPointerException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "system-error";
        return commonResolve (viewName, exception, request, response);
    }

    // @ExceptionHandler将一个具体的异常类型和一个方法关联起来
    private ModelAndView commonResolve(
            // 异常处理完成后要去的页面
            String viewName,

            // 实际捕获到的异常类型
            Exception exception,

            // 当前请求对象
            HttpServletRequest request,

            // 当前响应对象
            HttpServletResponse response) throws IOException {

        // 1.判断当前请求类型
        boolean judgeRequest = CrowdUtil.judgeRequestType (request);

        // 2.如果是Ajax请求
        if (judgeRequest) {

            // 3.创建ResultEntity对象
            ResultEntity<Object> failed = ResultEntity.failed (exception.getMessage ( ));

            // 4.创建Gson对象
            Gson gson = new Gson ( );
            // 5.将ResultEntity对象转换为JSON字符串
            String json = gson.toJson (failed);

            // 6.将JSON字符串作为响应体返回给浏览器
            response.getWriter ( ).write (json);

            // 7.由于上面已经通过原生的response对象返回了响应，所以不提供ModelAndView对象

            return null;
        }

        // 8.如果不是Ajax请求则创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView ( );

        // 9.将Exception对象存入模型
        modelAndView.addObject (CrowdConstant.ATTR_NAME_EXCEPTION, exception);

        // 10.设置对应的视图名称
        modelAndView.setViewName (viewName);

        // 11.返回modelAndView对象
        return modelAndView;
    }


}
